﻿//IMPORTANT: Remember to write about how you cannot change the Added and Removed collections with a Commit(vm) call while iterating through them
using System;
using Caffeine.Core;
using Caffeine.Examples.Repository;
using Caffeine.Wpf;
using Microsoft.Practices.Unity;

namespace Caffeine.Examples.Contacts.ViewModel
{
    public class ContactManagerViewModel : ViewModelBase
    {
        #region State

        private readonly IUnityContainer _container;
        private readonly ContactRepository _repository;
        private readonly ActionCommand _addContact;
        private readonly ActionCommand _saveContacts;
        private readonly ActionCommand _undoContactChanges;
        private readonly ViewModelProperty<ContactViewModel> _selectedContact;
        private readonly ViewModelCollection<ContactViewModel> _contacts;

        #endregion

        #region Constructors

        public ContactManagerViewModel(IUnityContainer container)
        {
            _container = container;
            _repository = Container.Resolve<ContactRepository>();
            _addContact = new ActionCommand(delegate { for (Int32 index = 0; index < 3; index++) { Contacts.Add(new ContactViewModel(Container)); } });
            _saveContacts = new ActionCommand(SaveAllContacts);
            _undoContactChanges = new ActionCommand(OnUndoContactChanges);
            _selectedContact = RegisterGenericProperty<ContactViewModel>("SelectedContact", null);
            _contacts = RegisterCollectionProperty<ContactViewModel>("Contacts");
            SelectedContact.PropertyChanged += delegate { RaisePropertyChanged<ContactManagerViewModel>(cmvm => cmvm.IsContactSelected); };

            Contacts.Remove(null);
        }

        #endregion

        #region Public

        public IUnityContainer Container
        {
            get
            {
                return _container;
            }
        }

        public ActionCommand AddContact
        {
            get
            {
                return _addContact;
            }
        }

        public ActionCommand SaveContacts
        {
            get
            {
                return _saveContacts;
            }
        }

        public ActionCommand UndoContactChanges
        {
            get
            {
                return _undoContactChanges;
            }
        }

        public Boolean IsContactSelected
        {
            get
            {
                return SelectedContact.Value != null;
            }
        }

        public ViewModelProperty<ContactViewModel> SelectedContact
        {
            get
            {
                return _selectedContact;
            }
        }
        
        public ViewModelCollection<ContactViewModel> Contacts
        {
            get
            {
                return _contacts;
            }
        }

        public void Load()
        {
            foreach (ContactViewModel contact in Repository.GetContacts())
            {
                Contacts.AddCommitted(contact);
            }
        }

        public void SelectContact(ContactViewModel selectedContact)
        {
            if (SelectedContact.Value != selectedContact)
            {
                foreach (ContactViewModel contact in Contacts)
                {
                    if (contact != selectedContact)
                    {
                        contact.IsSelected.Value = false;
                    }
                }

                if (selectedContact != null)
                {
                    selectedContact.IsSelected.Value = true;

                    if (!selectedContact.IsHydrated.Value)
                    {
                        if (!selectedContact.IsAddedToCollection)
                        {
                            HydrateContact(selectedContact);
                        }

                        selectedContact.IsHydrated.Value = true;
                    }
                }

                SelectedContact.Value = selectedContact;
            }
        }

        public void RemoveContact(ContactViewModel contactToRemove)
        {
            Int32 removedContactIndex = Contacts.IndexOf(contactToRemove);

            Contacts.Remove(contactToRemove);

            if (Contacts.Count == 0)
            {
                SelectContact(null);
            }
            else if (SelectedContact.Value == contactToRemove)
            {
                SelectContact(Contacts[Math.Min(removedContactIndex, Contacts.Count - 1)]);
            }
        }

        #endregion

        #region Private

        private ContactRepository Repository
        {
            get
            {
                return _repository;
            }
        }

        private void SaveAllContacts(Object parameter)
        {
            foreach (ContactViewModel contact in Contacts)
            {
                contact.SaveContact();
            }

            foreach (ContactViewModel contact in Contacts.GetRemovedViewModels())
            {
                contact.RemoveDependencies();
                Repository.RemoveContact(contact);
            }

            //IMPORTANT: You cannot commit view models as you're iterating over the AddedViewModels and RemovedViewModels
            //collections. If you do, you'll get an exception saying the collection has been modified.  Perform all necessary
            //repository operations then commit the entire collection.
            Contacts.Commit();
        }

        private void OnUndoContactChanges(Object parameter)
        {
            this.ResetGraph();
        }

        private void HydrateContact(ContactViewModel contact)
        {
            foreach (AddressViewModel address in Repository.GetContactAddresses(contact))
            {
                contact.Addresses.AddCommitted(address);
            }
        }

        #endregion
    }
}
